package genetic;


import java.util.*;
import java.util.stream.Collectors;

/**
 * 遗传算法核心逻辑
 */
public class GeneticAlgorithm {

    /**
     * 初始化遗传算法
     */
    public GeneticAlgorithm(GeneticModel geneticModel, int jobNumber, int stationNumber) {
        // 初始化工单数量
        geneticModel.setWorkOrderNumber(jobNumber);
        // 初始化工位数量
        geneticModel.setStationNumber(stationNumber);
    }

    /**
     * 遗传算法主逻辑
     * 1.用随机方法，先制定出一个字符串，用来表示工单的加工顺序，定为染色体
     * 2.制定多组染色体存入一个集群
     * 3.挑选集群中的一部分染色体，计算这部分染色体中的最优时间，挑选两组进行遗传繁衍（交叉算子）或者进行变异（变异算子）
     * 若计算出最优时间为最优解则终止遗传，输出结果
     * 4.挑选集群中的一部分染色体，计算这部分染色体中的最坏时间进行淘汰，优化存货集群（优化算法，暂未实现）
     * 5.将繁衍后的染色体加入到集群，重复3至多代
     * 6.获取最优计算时间
     */
    public Result run(GeneticModel geneticModel, List<List<MesStationJobBo>> jobList, Long minTime) {
        // 获取工单数量
        Map<String, Map<Long, String>> stationMatrixMap = new HashMap<>();
        Map<String, Map<Long, Long>> timeMatrixMap = new HashMap<>();
        Map<String, List<Long>> processNewSortMap = new HashMap<>();
        // 循环每个工单，取工单中工序最多的工序作为工序最大值使用
        // 设定工位和工位用时的时间
        for (List<MesStationJobBo> mesStationJobBoList : jobList) {
            geneticModel.setChromosomeSize(geneticModel.getChromosomeSize() + mesStationJobBoList.size());
            geneticModel.setProcessNumber(Math.max(geneticModel.getProcessNumber(), mesStationJobBoList.size()));
            Map<Long, String> stationMatrixInnerMap = new HashMap<>();
            Map<Long, Long> timeMatrixInnerMap = new HashMap<>();
            List<Long> processSortList = new ArrayList<>();
            for (MesStationJobBo mesStationJobBo : mesStationJobBoList) {
                stationMatrixInnerMap.put(mesStationJobBo.getProcessSortData(), mesStationJobBo.getWorkStationIdData());
                timeMatrixInnerMap.put(mesStationJobBo.getProcessSortData(), mesStationJobBo.getWorkStationTimeData());
                processSortList.add(mesStationJobBo.getProcessSortData());
            }
            stationMatrixMap.put(mesStationJobBoList.get(0).getWorkOrderIdData(), stationMatrixInnerMap);
            timeMatrixMap.put(mesStationJobBoList.get(0).getWorkOrderIdData(), timeMatrixInnerMap);
            processNewSortMap.put(mesStationJobBoList.get(0).getWorkOrderIdData(),processSortList);
        }
        geneticModel.setStationMatrixMap(stationMatrixMap);
        geneticModel.setTimeMatrixMap(timeMatrixMap);
        geneticModel.setProcessSortMap(processNewSortMap);
        // 初始化基因种群
        this.initialPopulation(geneticModel, jobList);

        // 遍历基因种群
        Set<Gene> geneSet = geneticModel.getGeneSet();
        for (int i = 0; i < geneticModel.getPopulationNumber(); i++) {
            // 获取随机数，按照设定值，进行变异或者遗传
            double p = (double) geneticModel.getRandom().nextInt(100) / 100.0;
            if (p < geneticModel.getMutationProbability()) {
                // 随机选择一个基因种群中的染色体
                int index = geneticModel.getRandom().nextInt(geneticModel.getGeneSet().size());
                int k = 0;
                for (Gene gene : geneticModel.getGeneSet()) {
                    if (k == index) {
                        // 基因突变
                        this.mutationGene(geneticModel, gene);
                        break;
                    }
                    k++;
                }
            } else {
                // 挑选两个集群的最优个体进行繁殖下一代
                Gene g1 = this.selectGene(geneticModel), g2 = this.selectGene(geneticModel);
                // 如果最优个体中出现最优解，则结束遗传
                if (Objects.equals(this.calculateFitness(geneticModel, g1).getFulfillTime(), minTime) || Objects.equals(this.calculateFitness(geneticModel, g2).getFulfillTime(), minTime)) {
                    break;
                }
                // 两个最优个体相互繁衍下一代，并加入到基因群众
                Gene child1 = this.crossGene(geneticModel, g1, g2), child2 = this.crossGene(geneticModel, g2, g1);
                geneSet.add(child1);
                geneSet.add(child2);
            }
        }
        geneticModel.setGeneSet(geneSet);
        // 遍历所有集群，寻找最优解
        Gene bestGene = new Gene(0xffffff);
        for (Gene gene : geneticModel.getGeneSet()) {
            if (bestGene.getFitness() > gene.getFitness()) {
                bestGene = gene;
            }
        }
        // 计算适应度
        return this.calculateFitness(geneticModel, bestGene);
    }


    /**
     * 初始化种群
     */
    public void initialPopulation(GeneticModel geneticModel, List<List<MesStationJobBo>> jobList) {
        Set<Gene> geneSet = geneticModel.getGeneSet();
        for (int i = 0; i < geneticModel.getPopulationNumber(); i++) {
            Gene g = new Gene();
            int size = 0;
            for (List<MesStationJobBo> jopSmall : jobList) {
                size += jopSmall.size();
            }
            List<Integer> indexList = makeList(size);
            // 初始化染色体长度
            String[] chromosome = new String[size];
            // 遍历工单和工序，将工序随机插入到染色体中
            for (List<MesStationJobBo> mesStationJobBoList : jobList) {
                for (MesStationJobBo mesStationJobBo : mesStationJobBoList) {
                    int index = geneticModel.getRandom().nextInt(indexList.size());
                    int val = indexList.remove(index);
                    // 从 MesStationJobBo 类的 workOrderIdData 中取值并添加到染色体中
                    chromosome[val] = mesStationJobBo.getWorkOrderIdData();
                }
            }
            g.setChromosome(Arrays.asList(chromosome));
            // 计算适应度
            g.setFitness(calculateFitness(geneticModel, g).getFulfillTime());
            // 将染色体插入到种群中
            geneSet.add(g);
        }
        geneticModel.setGeneSet(geneSet);
    }

    /**
     * 计算适应度
     */
    public Result calculateFitness(GeneticModel geneticModel, Gene gene) {
        Result result = new Result();
        Map<String, Long> stationWorkTimeMap = result.getStationWorkTime();
        //开始时间集合
        Map<String, Map<Long, Long>> startTimeMap = new HashMap<>();
        //结束时间集合
        Map<String, Map<Long, Long>> endTimeMap = new HashMap<>();
        Map<String, Integer> processNewSortMap = new HashMap<>();
        Map<String, List<Long>> processSortMap = geneticModel.getProcessSortMap();
        // 对每个List进行排序
        processSortMap.forEach((key, value) -> value.sort(Long::compareTo));
        for (String workOrderId : gene.getChromosome()) {
            List<Long> sortList = geneticModel.getProcessSortMap().get(workOrderId);
            // 获取工单的工序
            Integer sort = processNewSortMap.getOrDefault(workOrderId, 0);
            Long processSort = sortList.get(Math.toIntExact(sort));
            // 获取加工需要的时间
            Long time = geneticModel.getTimeMatrixMap().get(workOrderId).get(processSort);
            //根据工单和工序取工位号
            String stationId = geneticModel.getStationMatrixMap().get(workOrderId).get(processSort);
            Long stationWorkTime = result.getStationWorkTime().getOrDefault(stationId, 0L);
            // 当工序为第一道工序的时候，开始时间为工位时间，否则取工位时间和上一道工序工位的结束时间的比较的最大值
            Map<Long, Long> startInnerMap = new HashMap<>();
            if (startTimeMap.get(workOrderId) != null) {
                startTimeMap.get(workOrderId).put(processSort, Objects.equals(processSort, sortList.get(0)) ? stationWorkTime :
                        Math.max(endTimeMap.getOrDefault(workOrderId, new HashMap<>()).getOrDefault(sortList.get(sort-1), 0L), stationWorkTime));
            } else {
                startInnerMap.put(processSort, Objects.equals(processSort, sortList.get(0)) ? stationWorkTime :
                        Math.max(endTimeMap.getOrDefault(workOrderId, new HashMap<>()).getOrDefault(sortList.get(sort-1), 0L), stationWorkTime));
                startTimeMap.put(workOrderId, startInnerMap);
            }
            // 工位时间更新为工序开始时间加上工序的加工时间
            Long startTime = startTimeMap.get(workOrderId).get(processSort);
            processNewSortMap.put(workOrderId, processNewSortMap.getOrDefault(workOrderId, 0) + 1);
            Long stationWorkTime4StationId = time + startTime;
            stationWorkTimeMap.put(stationId, stationWorkTime4StationId);
            // 工单的工序结束时间
            Map<Long, Long> endInnerMap = new HashMap<>();
            if (endTimeMap.get(workOrderId) != null) {
                endTimeMap.get(workOrderId).put(processSort, stationWorkTimeMap.get(stationId));
            } else {
                endInnerMap.put(processSort, stationWorkTimeMap.get(stationId));
                endTimeMap.put(workOrderId, endInnerMap);
            }
            // 最大时间为适应度时间
            result.setFulfillTime(Math.max(result.getFulfillTime(), stationWorkTimeMap.get(stationId)));

        }
        result.setStartTimeMap(startTimeMap);
        result.setEndTimeMap(endTimeMap);
        result.setStationWorkTime(stationWorkTimeMap);
        return result;
    }

    /**
     * 交叉算子
     * 对于一对染色体g1, g2，首先随机产生一个起始位置start和终止位置end
     * 并由从g1的染色体序列从start到end的序列中产生一个子代
     */
    private Gene crossGene(GeneticModel geneticModel, Gene g1, Gene g2) {
        List<Integer> indexList = makeList(geneticModel.getChromosomeSize());
        // 随机产生两个位置
        int p1 = indexList.remove(geneticModel.getRandom().nextInt(indexList.size()));
        int p2 = indexList.remove(geneticModel.getRandom().nextInt(indexList.size()));
        // 设定开始位置和结束位置
        int start = Math.min(p1, p2);
        int end = Math.max(p1, p2);
        // 获取染色体原型
        List<String> proto = subArray(g1.getChromosome(), start, end + 1);
        // 复制2号染色体
        List<String> newList = new ArrayList<>(g2.getChromosome());
        // 将原型工单在2号染色体中删除
        for (String val : proto) {
            for (int i = 0; i < newList.size(); i++) {
                if (val.equals(newList.get(i))) {
                    newList.remove(i);
                    break;
                }
            }
        }

        // 将原型工单，插入到2号染色体中
        Gene child = new Gene();
        proto.addAll(newList.subList(start, newList.size()));
        List<String> childGene = newList.subList(0, start);
        childGene.addAll(proto);
        // 整理新的染色体
        child.setChromosome(childGene);
        child.setFitness(calculateFitness(geneticModel, child).getFulfillTime());
        return child;
    }

    /**
     * 突变算子
     * 变异的作用主要是使算法能跳出局部最优解，因此不同的变异方式对算法能否求得全局最优解有很大的影响。
     * 使用位置变异法作为变异算子，即从染色体中随机产生两个位置并交换这两个位置的值
     */
    public void mutationGene(GeneticModel geneticModel, Gene gene) {
        List<Integer> indexList = makeList(geneticModel.getChromosomeSize());
        List<String> chromosome = gene.getChromosome();
        for (int i = 0; i < geneticModel.getMutationGeneNum(); i++) {
            // 随机选择位置并进行交换
            int a = -1, b = -1;
            if (!indexList.isEmpty()) {
                a = indexList.remove(geneticModel.getRandom().nextInt(indexList.size()));
            }
            if (!indexList.isEmpty()) {
                b = indexList.remove(geneticModel.getRandom().nextInt(indexList.size()));
            }
            if (a >= 0 && b >= 0) {
                String t = chromosome.get(a);
                chromosome.set(a, chromosome.get(b));
                chromosome.set(b, t);
            }
        }
        gene.setChromosome(chromosome);
        // 计算适应度
        gene.setFitness(this.calculateFitness(geneticModel, gene).getFulfillTime());
    }

    /**
     * 选择最优个体，用于遗传
     * 在基因种群中选择一部分群体，个体竞争，选择最好的个体
     */
    public Gene selectGene(GeneticModel geneticModel) {
        List<Integer> indexList = makeList(geneticModel.getGeneSet().size());
        Map<Integer, Boolean> map = new HashMap<>();
        for (int i = 0; i < geneticModel.getSelectGeneNum(); i++) {
            map.put(indexList.remove(geneticModel.getRandom().nextInt(indexList.size())), true);
        }
        Gene bestGene = new Gene(0xfffff);
        int i = 0;
        // 遍历选择集合染色体的适应度，选取最好的
        for (Gene gene : geneticModel.getGeneSet()) {
            if (map.containsKey(i)) {
                if (bestGene.getFitness() > gene.getFitness()) {
                    bestGene = gene;
                }
            }
            i++;
        }
        return bestGene;
    }

    /**
     * 计算当前工序和工位生产关系的最优解
     * 最优解算法为，计算单个工位的满负荷工作时间，满负荷的最大值就是理论上的最优解的总耗时时间
     *
     * @param geneticModel 工位时间
     * @return 最大时长
     */
    public static Long optimalSolution(GeneticModel geneticModel, List<List<MesStationJobBo>> jobList, List<String> firstNumbers) {
        // 获取最优解的工位时长
        long minTime = 0L;
        // 循环所有工位
        for (int i = 0; i < geneticModel.getStationNumber(); i++) {
            long stationMinTime = 0L;
            // 遍历生产基本信息，获取对应工位的加工总时长
            for (List<MesStationJobBo> list : jobList) {
                for (MesStationJobBo job : list) {
                    stationMinTime = stationMinTime + (Objects.equals(job.getWorkStationIdData(), firstNumbers.get(i)) ? job.getWorkStationTimeData() : 0);
                }
            }
            // 获取最大时长
            minTime = Math.max(minTime, stationMinTime);
        }
        return minTime;
    }

    /**
     * 创建list
     */
    private List<Integer> makeList(int n) {
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            result.add(i);
        }
        return result;
    }

    /**
     * 截取目标List
     */
    public List<String> subArray(List<String> arr, int start, int end) {
        return new ArrayList<>(arr.subList(start, end));
    }

}
